/***************************************************************************
 *   Copyright (C) 2008 by Tyler Shaub   *
 *   tyler.shaub@gmail.com   *
 *                                                                         *
 *   See COPYING	*
 ***************************************************************************/

#include "TypeInfo.hpp"

#include <map>

#include <boost/pool/pool_alloc.hpp>

#include "TypeManager.hpp"

#include "AnyPtr.hpp"
#include "MemberFunctions.hpp"
#include "MemberVariables.hpp"

namespace Flex_ {

	namespace {
		const char INVALID_NAME[] = "FLEX_INVALID_TYPE_INFO_NAME";
		typedef std::map<TypeId, CastFn> CastMap;
	}

	// pimpl is a workaround for dependencies (mostly due to templates usage in mem funcs)
	struct TypeInfo::Impl
	{
		TypeId typeId_;	// generated by type manager
		std::string name_;	// user defined
		size_t size_;	// sizeof

		TypeId nonPtrTypeId_;

		TypeInfo::TypeSet interfaces_;
		TypeInfo::TypeSet implementations_;

		mutable CastMap casts_;

		MemberFunctions functions_;
		MemberVariables variables_;

		CloneFn cloneFn_;
		CreateFn createFn_;
		DestroyFn destructFn_, destroyFn_;
	};

	struct InfoImplTag { };
//(boost::singleton_pool<InfoImplTag, sizeof(Impl)>::malloc())
TypeInfo::TypeInfo(TypeId id)
: impl_( new Impl() )
{
	impl_->name_ = INVALID_NAME;
	impl_->size_ = 0;
	impl_->nonPtrTypeId_ = INVALID_TYPE;
	setType(id);
}

TypeInfo::TypeInfo(TypeInfo const& cpy)
	: impl_( new Impl(*cpy.impl_) )
{
}

TypeInfo& TypeInfo::operator=(TypeInfo const& rhs)
{
	if(impl_.get() != rhs.impl_.get())
		*impl_ = *rhs.impl_;
	return *this;
}

TypeInfo::~TypeInfo()
{
	/*impl_.get()->~Impl();
	boost::singleton_pool<InfoImplTag, sizeof(Impl)>::free(impl_.get());
	impl_.release();*/
}

void TypeInfo::swap(TypeInfo & other)
{
	//using std::swap;
	//swap(impl_, other.impl_);
    std::auto_ptr<Impl> tmp = impl_;
    impl_ = other.impl_;
    other.impl_ = tmp;
}

void TypeInfo::SetSize(size_t size)
{
	impl_->size_ = size;
}

void TypeInfo::SetName(char const* name)
{
	impl_->name_ = name;
}

void TypeInfo::SetReferenceType(TypeId id)
{
	assert(id != INVALID_TYPE); // get it right!
	assert(impl_->nonPtrTypeId_ == INVALID_TYPE); // get it right (the first time)!
	impl_->nonPtrTypeId_ = id;
}

TypeId TypeInfo::GetId() const
{
	return impl_->typeId_;
}

std::string const& TypeInfo::GetName() const
{
	return impl_->name_;
}

size_t TypeInfo::GetSize() const
{
	return impl_->size_;
}

TypeId TypeInfo::GetReferenceTypeId() const
{
	return impl_->nonPtrTypeId_;
}

void TypeInfo::setType(TypeId type)
{
	impl_->typeId_ = type;

	impl_->interfaces_.insert(type);
	impl_->implementations_.insert(type);
}

const bool TypeInfo::ImplementsInterface(TypeId faceId) const
{
	return impl_->interfaces_.find(faceId) != impl_->interfaces_.end();
}

TypeInfo::TypeSet const& TypeInfo::GetInterfaces() const
{
	return impl_->interfaces_;
}

TypeInfo::TypeSet const& TypeInfo::GetImplementations() const
{
	return impl_->implementations_;
}

void TypeInfo::AddInterface(TypeInfo const& base)
{
	impl_->interfaces_.insert(base.impl_->interfaces_.begin(),
							   base.impl_->interfaces_.end());
}

void TypeInfo::AddImplementation(TypeId impl)
{
	impl_->implementations_.insert(impl);
}

void TypeInfo::SetFunctions(MemberFunctions const& fns)
{
	// assert empty... uh oh, this makes sense, but means we can't add the base/derived functions
	assert( impl_->functions_.GetAllFunctions().first == impl_->functions_.GetAllFunctions().second );

	impl_->functions_ = fns;
}

MemberFunctions const& TypeInfo::GetFunctions() const
{
	return impl_->functions_;
}

void TypeInfo::SetVariables(MemberVariablePtrMap const& vars)
{
	impl_->variables_.SetVariables(vars);
}

MemberVariables const& TypeInfo::GetVariables() const
{
	return impl_->variables_;
}


void TypeInfo::SetCreationFunctions(CreateFn const& createFn,
						CloneFn const& cloneFn,
						DestroyFn const& destructFn,
						DestroyFn const& destroyFn)
{
	impl_->createFn_ = createFn;
	impl_->cloneFn_ = cloneFn;
	impl_->destructFn_ = destructFn;
	impl_->destroyFn_ = destroyFn;
}

void TypeInfo::AddCast(TypeId toType, CastFn const& cast) const
{
	assert( impl_->casts_.find(toType) == impl_->casts_.end() ||
			impl_->casts_.find(toType)->second == cast );
	impl_->casts_[toType] = cast;
}

void* TypeInfo::cast(void* mePtr, TypeId toType) const
{
	if(impl_->typeId_ == toType) return mePtr;

	CastMap::const_iterator i = impl_->casts_.find( toType );

	// if cast not registered/invalid return NULL
	return i != impl_->casts_.end()? ((i->second)(mePtr)) : 0;
}

void const* TypeInfo::cast(void const* mePtr, TypeId toType) const
{
	return cast( const_cast<void*>(mePtr), toType );
}

void* TypeInfo::create() const { return impl_->createFn_(); }
void* TypeInfo::clone(void const* cpy) const { return impl_->cloneFn_(cpy); }
void TypeInfo::destroy(void * p) const { impl_->destroyFn_(p); }

AnyPtr TypeInfo::CreateAny() const
{
	return AnyPtr(impl_->createFn_(), this);
}

AnyPtr TypeInfo::CopyAny(AnyPtr const& ptr) const
{
	assert(ptr.GetTypeId() == impl_->typeId_); // wtf wrong copy func!

	return AnyPtr(impl_->cloneFn_(ptr.getUnsafePtr()), this);
}

void TypeInfo::DestroyAny(AnyPtr ptr) const
{
	assert(ptr.GetTypeId() == impl_->typeId_);

	impl_->destroyFn_(ptr.getUnsafePtr());
}

}

// debug function

#include <iostream>
#include <iterator>
#include <algorithm>

namespace Flex_ {
void TypeInfo::Print() const // funky placement... debugging only (?)
{
	using std::cout;
	using std::endl;

	cout << "TypeInfo for: \"" << impl_->name_ << "\"" << endl;

	cout << "Size: " << impl_->size_ << endl;

	cout << "All types/interfaces: ";
	std::copy(impl_->interfaces_.begin(),
		impl_-> interfaces_.end(), std::ostream_iterator<int>(cout, " "));

	cout << endl;

	cout << "All implementations: ";
	std::copy(impl_->implementations_.begin(),
		impl_->implementations_.end(), std::ostream_iterator<int>(cout, " "));
	cout << endl << endl;
}

}
